#region Using

using System;
using System.Collections.ObjectModel;
using System.Data;
using System.Net;
using System.Text;
using System.Xml;

#endregion

namespace AvalonEdit.Sample
{
    /// <summary>
    /// Summary description for HtmlValidator
    /// </summary>
    public class HtmlValidator : IDisposable
    {
        #region Constructor

        public HtmlValidator(string url)
        {
            _url = url;
        }

        #endregion

        #region Properties

        private string _url;

        /// <summary>
        /// The Url to validate.
        /// </summary>
        public string Url
        {
            get { return _url; }
            set { _url = value; }
        }

        private string _docType;

        /// <summary>
        /// The Doctype of the validated document.
        /// </summary>
        public string Doctype
        {
            get { return _docType; }
        }

        private Encoding _charset;

        /// <summary>
        /// The character set of the validated document.
        /// </summary>
        public Encoding Charset
        {
            get { return _charset; }
        }

        private readonly Collection<ValidationError> _errors = new Collection<ValidationError>();

        /// <summary>
        /// The errors returned from the validator.
        /// </summary>
        public Collection<ValidationError> Errors
        {
            get { return _errors; }
        }

        private readonly Collection<ValidationError> _warnings = new Collection<ValidationError>();

        /// <summary>
        /// The warnings returned from the validator.
        /// </summary>
        public Collection<ValidationError> Warnings
        {
            get { return _warnings; }
        }

        /// <summary>
        /// Gets whether or not the document validates.
        /// </summary>
        public bool IsValid
        {
            get { return _errors.Count == 0; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Validate the URL against the W3C standards
        /// </summary>
        /// <returns>True if the document validates.</returns>
        public bool Validate()
        {
            const string url = "Url";
            if (string.IsNullOrEmpty(Url))
                throw new ArgumentNullException(url);

            using (DataSet ds = RetrieveData())
            {
                if (ds.Tables.Contains("Fault"))
                    throw new WebException("The specified Url does not exist", WebExceptionStatus.NameResolutionFailure);

                ParseMetaData(ds.Tables[1]);
                ParseErrors(ds.Tables[4], _errors);
                ParseErrors(ds.Tables[7], _warnings);
            }
            
            
            return IsValid;
        }

        private DataSet RetrieveData()
        {
            string url = "http://validator.w3.org/check?uri=" + Url + "&output=soap12";
            using (XmlReader reader = XmlReader.Create(url))
            {
                var ds = new DataSet();
                ds.ReadXml(reader);
                return ds;
            }
        }

        private void ParseMetaData(DataTable table)
        {
            DataRow row = table.Rows[0];
            _docType = row["doctype"].ToString();
            _charset = Encoding.GetEncoding(row["charset"].ToString());
        }

        private void ParseErrors(DataTable table, Collection<ValidationError> col)
        {
            foreach (DataRow row in table.Rows)
            {
                if (row.Table.Columns.Contains("line") && row["line"].ToString() != string.Empty)
                {
                    int line = int.Parse(row["line"].ToString());
                    int column = int.Parse(row["col"].ToString());
                    string message = row["message"].ToString();
                    col.Add(new ValidationError(line, column, message));
                }
            }
        }

        #endregion

        #region IDisposable Members

        private bool _isDisposed;

        private void Dispose(bool disposing)
        {
            if (disposing && !_isDisposed)
            {
                _errors.Clear();
                _warnings.Clear();
                _docType = null;
                _charset = null;
                _url = null;
                _isDisposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        #region ValidationError struct

        public struct ValidationError
        {
            public ValidationError(int lineNumber, int columnIndex, string message)
            {
                _lineNumber = lineNumber;
                _columnIndex = columnIndex;
                _message = message;
            }

            #region Properties

            private readonly int _lineNumber;

            /// <summary>
            /// The line number in the HTML file.
            /// </summary>
            public int LineNumber
            {
                get { return _lineNumber; }
            }

            private readonly int _columnIndex;

            /// <summary>
            /// The column index of the line.
            /// </summary>
            public int ColumnIndex
            {
                get { return _columnIndex; }
            }

            private readonly string _message;

            /// <summary>
            /// A description of the error.
            /// </summary>
            public string Message
            {
                get { return _message; }
            }

            #endregion
        }

        #endregion
    }
}